using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace DemonstrationWebsite.Utils
{
    /// <summary>
    /// This control produces a tabbed and syntax-highlighted view of a set of source files
    /// Achieving the 100%-height behaviour is fiddly, and for that reason, this control is unlikely
    /// to work successfully unless it's the only control on the page, and the page is designed to 
    /// work with it. 
    /// ---
    /// In other words, you probably don't want to re-use this in other projects.
    /// </summary>
    [ParseChildren(true, "FileTabs")]
    public class SourceCodeViewer : Control, INamingContainer
    {
        private const string DemoExclusionRegex = @"\<!-- begin_demo_exclude --\>.*?\<!-- end_demo_exclude --\>\n*";

        // Supports declarative syntax for including a list of tabs
        private List<FileTab> m_FileTabs = new List<FileTab>();
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<FileTab> FileTabs
        {
            get { return m_FileTabs; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            // Get the supporting JS library
            Page.ClientScript.RegisterClientScriptInclude(GetType(), "SourceCodeViewer.js", VirtualPathUtility.ToAbsolute("~/Utils/SourceCodeViewer.js"));

            // Get CSS/JS for the syntax highlighter
            Page.ClientScript.RegisterClientScriptInclude(GetType(), "shCore.js", VirtualPathUtility.ToAbsolute("~/Utils/SyntaxHighlighting/Scripts/shCore.js"));
            Page.ClientScript.RegisterClientScriptInclude(GetType(), "shBrushCSharp.js", VirtualPathUtility.ToAbsolute("~/Utils/SyntaxHighlighting/Scripts/shBrushCSharp.js"));
            Page.ClientScript.RegisterClientScriptInclude(GetType(), "shBrushJScript.js", VirtualPathUtility.ToAbsolute("~/Utils/SyntaxHighlighting/Scripts/shBrushJScript.js"));
            Page.ClientScript.RegisterClientScriptInclude(GetType(), "shBrushXml.js", VirtualPathUtility.ToAbsolute("~/Utils/SyntaxHighlighting/Scripts/shBrushXml.js"));
            Page.ClientScript.RegisterClientScriptBlock(GetType(), "CSS", string.Format("<link type=\"text/css\" rel=\"stylesheet\" href=\"{0}\"/>", VirtualPathUtility.ToAbsolute("~/Utils/SyntaxHighlighting/Styles/SyntaxHighlighter.css")), false);
            base.OnPreRender(e);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "TabRow");
            writer.RenderBeginTag(HtmlTextWriterTag.Ul);

            // Put a caption as the first list item
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            writer.Write("Click to view source code: &nbsp;");
            writer.RenderEndTag();

            // Render the tabs themselves
            foreach (FileTab tab in FileTabs)
            {
                string tabClientID = GetTabClientID(tab);
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "Tab");
                writer.AddAttribute(HtmlTextWriterAttribute.Id, tabClientID + "_title");
                writer.RenderBeginTag(HtmlTextWriterTag.Li);

                writer.AddAttribute(HtmlTextWriterAttribute.Href, string.Format("javascript:SourceViewer_showSourceCodeTab('{0}')", tabClientID));
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.Write(tab.Name);
                writer.RenderEndTag();
                
                writer.RenderEndTag();
            }
            writer.RenderEndTag(); // UL

            // Render each of the tab content panes (initially invisible)
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "TabContentsScroller");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            foreach (FileTab tab in FileTabs)
            {
                writer.AddAttribute("IsTabContents", "true");
                writer.AddAttribute(HtmlTextWriterAttribute.Class, tab.Language.ToString());
                writer.AddAttribute(HtmlTextWriterAttribute.Id, GetTabClientID(tab));
                writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
                RenderTabContents(tab, writer);
                writer.RenderEndTag();
            }
            writer.RenderEndTag();
        }

        private string GetTabClientID(FileTab tab)
        {
            return string.Format("{0}_tab_{1}", this.ClientID, tab.GetHashCode());
        }

        private void RenderTabContents(FileTab tab, HtmlTextWriter writer)
        {
            // Locate the physical filename of the source file
            string currentASPXFile = HttpContext.Current.Server.MapPath(HttpContext.Current.Request.Url.PathAndQuery);
            currentASPXFile = currentASPXFile.Substring(0, currentASPXFile.LastIndexOf("\\"));
            string requestedFile = Path.Combine(currentASPXFile, tab.Source);

            // Render in a PRE without leading tabs
            string name = "code_" + tab.GetHashCode();
            writer.Write("<PRE name=\""+name+"\" class=\""+ tab.Language.ToString().ToLower() +":nocontrols\">");
            using (StreamReader sr = new StreamReader(requestedFile))
            {
                // Remove anything that's supposed to be excluded from the demo
                string code = sr.ReadToEnd().Trim();
                code = Regex.Replace(code, DemoExclusionRegex, "", RegexOptions.Singleline);
                writer.Write(HttpUtility.HtmlEncode(code));
            }
            writer.Write("</PRE>");

            // Append JS to invoke script highlighter
            writer.Write(string.Format("<script>dp.SyntaxHighlighter.HighlightAll('{0}')</script>", name));
        }
    }
    
    public enum Language : int
    {
        CSharp, HTML, JScript
    }

    public class FileTab : Control
    {
        private string m_Name;
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        private string m_Source;
        public string Source
        {
            get { return m_Source; }
            set { m_Source = value; }
        }

        private Language m_Language;
        public Language Language
        {
            get { return m_Language; }
            set { m_Language = value; }
        }
    }
}
